#include "icm20608.h"

struct icm20608_original {
	int16_t gyro_x_adc;		/* 陀螺仪X轴原始值 */
	int16_t gyro_y_adc;		/* 陀螺仪Y轴原始值 */
	int16_t gyro_z_adc;		/* 陀螺仪Z轴原始值 */
	int16_t accel_x_adc;	/* 加速度计X轴原始值 */
	int16_t accel_y_adc;	/* 加速度计Y轴原始值 */
	int16_t accel_z_adc;	/* 加速度计Z轴原始值 */
	int16_t temp_adc;		/* 温度原始值 */
};

struct icm20608_dev {
	struct spi_device *spi;		/* spi设备 */
	dev_t devid;				/* 设备号 */
	struct cdev cdev;			/* cdev */
	struct class *class;		/* 类 */
	struct device *device;		/* 设备 */
};

/*
 * @description	: 从icm20608读取多个寄存器数据
 * @param - dev: icm20608设备
 * @param - reg: 要读取的寄存器首地址
 * @param - val: 读取到的数据
 * @param - len: 要读取的数据长度
 * @return: 操作结果
 */
static int icm20608_read_regs(struct spi_device *spi, uint8_t reg, void *buf, uint32_t len)
{
	uint8_t txdata[1];
	struct spi_message m;
	struct spi_transfer t[2];
	
	//复位spi_transfer
	memset(&t, 0, sizeof(t));
	
	//初始化spi_message
	spi_message_init(&m);

	//写数据的时候首寄存器地址bit8要置1
	txdata[0] = reg | 0x80;
	//发送缓存
	t[0].tx_buf = txdata;
	//接收缓存
	t[0].rx_buf = NULL;
	//传输的长度
	t[0].len = 1;
	//将spi_transfer添加到spi_message队列
	spi_message_add_tail(&t[0], &m);

	//发送缓存
	t[1].tx_buf = NULL;
	//接收缓存
	t[1].rx_buf = buf;
	//传输的长度
	t[1].len = len;
	//将spi_transfer添加到spi_message队列
	spi_message_add_tail(&t[1], &m);

	//同步传输
	return spi_sync(spi, &m);
}

/*
 * @description	: 向icm20608多个寄存器写入数据
 * @param - dev: icm20608设备
 * @param - reg: 要写入的寄存器首地址
 * @param - val: 要写入的数据缓冲区
 * @param - len: 要写入的数据长度
 * @return : 操作结果
 */
static int icm20608_write_regs(struct spi_device *spi, uint8_t reg, uint8_t *buf, uint32_t len)
{
	unsigned char txdata[1];
	struct spi_message m;
	struct spi_transfer t[2];

	//复位spi_transfer
	memset(&t, 0, sizeof(t));
	
	//初始化spi_message
	spi_message_init(&m);
	
	//读数据的时候首寄存器地址bit8要置0
	txdata[0] = reg & ~0x80;
	//发送缓存
	t[0].tx_buf = txdata;
	//接收缓存
	t[0].rx_buf = NULL;
	//传输的长度
	t[0].len = 1;
	//将spi_transfer添加到spi_message队列
	spi_message_add_tail(&t[0], &m);

	//发送缓存
	t[1].tx_buf = buf;
	//接收缓存
	t[1].rx_buf = NULL;
	//传输的长度
	t[1].len = len;
	//将spi_transfer添加到spi_message队列
	spi_message_add_tail(&t[1], &m);

	//同步传输
	return spi_sync(spi, &m);
}

/*
 * @description	: 读取icm20608指定寄存器值，读取一个寄存器
 * @param - dev: icm20608设备
 * @param - reg: 要读取的寄存器
 * @return : 读取到的寄存器值
 */
static int icm20608_read_reg(struct spi_device *spi, uint8_t reg, uint8_t *data)
{
	return icm20608_read_regs(spi, reg, data, 1);
}

/*
 * @description	: 向icm20608指定寄存器写入指定的值，写一个寄存器
 * @param - dev: icm20608设备
 * @param - reg: 要写的寄存器
 * @param - data: 要写入的值
 * @return : 无
 */
static int icm20608_write_reg(struct spi_device *spi, uint8_t reg, uint8_t value)
{
	return icm20608_write_regs(spi, reg, &value, 1);
}

/*
 * @description	: 读取ICM20608的数据，读取原始数据，包括三轴陀螺仪、三轴加速度计和内部温度。
 * @param - dev	: ICM20608设备
 * @return : 无。
 */
void icm20608_read_data(struct icm20608_dev *dev, struct icm20608_original *icm20608)
{
	unsigned char data[14];

	icm20608_read_regs(dev->spi, ICM20_ACCEL_XOUT_H, data, 14);

	icm20608->accel_x_adc = (signed short)((data[0] << 8) | data[1]); 
	icm20608->accel_y_adc = (signed short)((data[2] << 8) | data[3]); 
	icm20608->accel_z_adc = (signed short)((data[4] << 8) | data[5]); 
	icm20608->temp_adc = (signed short)((data[6] << 8) | data[7]); 
	icm20608->gyro_x_adc = (signed short)((data[8] << 8) | data[9]); 
	icm20608->gyro_y_adc = (signed short)((data[10] << 8) | data[11]);
	icm20608->gyro_z_adc = (signed short)((data[12] << 8) | data[13]);
}

/*
 * @description : 打开设备
 * @param - inode : 传递给驱动的inode
 * @param - filp : 设备文件
 * @return : 0 成功;其他 失败
 */
static int icm20608_open(struct inode *inode, struct file *filp)
{
	struct cdev *cdev;

	cdev = inode->i_cdev;
	filp->private_data = container_of(cdev, struct icm20608_dev, cdev);
	return 0;
}

/*
 * @description : 从设备读取数据 
 * @param - filp : 要打开的设备文件(文件描述符)
 * @param - buf : 返回给用户空间的数据缓冲区
 * @param - lenght : 要读取的数据长度
 * @param - offt : 相对于文件首地址的偏移
 * @return : 读取的字节数，如果为负值，表示读取失败
 */
static ssize_t icm20608_read(struct file *filp, char __user *buf, size_t lenght, loff_t *off)
{
	struct icm20608_original icm20608_data;
	struct icm20608_dev *dev = filp->private_data;
	
	if(lenght < sizeof(icm20608_data))
		return -EINVAL;

	icm20608_read_data(dev, &icm20608_data);
	if(copy_to_user(buf, &icm20608_data, sizeof(icm20608_data))) 
		return -EFAULT;
	return sizeof(icm20608_data);
}

/*
 * @description : 关闭/释放设备
 * @param - filp : 要关闭的设备文件(文件描述符)
 * @return : 0 成功;其他 失败
 */
static int icm20608_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/* icm20608操作函数 */
static const struct file_operations icm20608_ops = {
	.owner = THIS_MODULE,
	.open = icm20608_open,
	.read = icm20608_read,
	.release = icm20608_release,
};

/*
 * ICM20608内部寄存器初始化函数 
 * @param - spi : 要操作的设备
 * @return : 无
 */
void icm20608_reginit(struct icm20608_dev *dev)
{
	u8 value;
	
	icm20608_write_reg(dev->spi, ICM20_PWR_MGMT_1, 0x80);
	mdelay(50);
	icm20608_write_reg(dev->spi, ICM20_PWR_MGMT_1, 0x01);
	mdelay(50);

	value = 0;
	icm20608_read_reg(dev->spi, ICM20_WHO_AM_I, &value);
	printk("ICM20608 ID = %#X\r\n", value);

	icm20608_write_reg(dev->spi, ICM20_SMPLRT_DIV, 0x00);		/* 输出速率是内部采样率 */
	icm20608_write_reg(dev->spi, ICM20_GYRO_CONFIG, 0x18);		/* 陀螺仪±2000dps量程 */
	icm20608_write_reg(dev->spi, ICM20_ACCEL_CONFIG, 0x18);		/* 加速度计±16G量程 */
	icm20608_write_reg(dev->spi, ICM20_CONFIG, 0x04);			/* 陀螺仪低通滤波BW=20Hz */
	icm20608_write_reg(dev->spi, ICM20_ACCEL_CONFIG2, 0x04);	/* 加速度计低通滤波BW=21.2Hz */
	icm20608_write_reg(dev->spi, ICM20_PWR_MGMT_2, 0x00);		/* 打开加速度计和陀螺仪所有轴 */
	icm20608_write_reg(dev->spi, ICM20_LP_MODE_CFG, 0x00);		/* 关闭低功耗 */
	icm20608_write_reg(dev->spi, ICM20_FIFO_EN, 0x00);			/* 关闭FIFO */
}

/*
  * @description : spi驱动的probe函数，当驱动与
  *					设备匹配以后此函数就会执行
  * @param - spi : spi设备
  * 
  */	
static int icm20608_probe(struct spi_device *spi)
{
	int ret;
	struct icm20608_dev *icm20608dev;

	printk("%s\r\n", __FUNCTION__);

	/* 分配icm20608dev对象的空间，devm表示模块卸载时自动释放 */
	icm20608dev = devm_kzalloc(&spi->dev, sizeof(*icm20608dev), GFP_KERNEL);
	if(!icm20608dev)
		return -ENOMEM;

	/* 1、创建设备号 */
	ret = alloc_chrdev_region(&icm20608dev->devid, 0, ICM20608_CNT, ICM20608_NAME);
	if(ret < 0)
	{
		pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", ICM20608_NAME, ret);
		return ret;
	}

	/* 2、初始化cdev */
	icm20608dev->cdev.owner = THIS_MODULE;
	cdev_init(&icm20608dev->cdev, &icm20608_ops);
	
	/* 3、添加一个cdev */
	ret = cdev_add(&icm20608dev->cdev, icm20608dev->devid, ICM20608_CNT);
	if(ret < 0)
	{
		unregister_chrdev_region(icm20608dev->devid, ICM20608_CNT);
		return ret;
	}
	
	/* 4、创建类 */
	icm20608dev->class = class_create(THIS_MODULE, ICM20608_NAME);
	if (IS_ERR(icm20608dev->class))
	{
		cdev_del(&icm20608dev->cdev);
		unregister_chrdev_region(icm20608dev->devid, ICM20608_CNT);
		return IS_ERR(icm20608dev->class);
	}

	/* 5、创建设备 */
	icm20608dev->device = device_create(icm20608dev->class, NULL, icm20608dev->devid, NULL, ICM20608_NAME);
	if (IS_ERR(icm20608dev->device))
	{
		class_destroy(icm20608dev->class);
		cdev_del(&icm20608dev->cdev);
		unregister_chrdev_region(icm20608dev->devid, ICM20608_CNT);
		return IS_ERR(icm20608dev->device);
	}
	icm20608dev->spi = spi;
	
	/* 初始化spi_device */
	spi->mode = SPI_MODE_0;	/*MODE0，CPOL=0，CPHA=0*/
	spi_setup(spi);
	
	/* 初始化ICM20608 */
	icm20608_reginit(icm20608dev);	
	/* 设置SPI设备的私有数据 */
	spi_set_drvdata(spi, icm20608dev);

	return 0;
}

/*
 * @description : spi驱动的remove函数，移除spi驱动的时候此函数会执行
 * @param - spi : spi设备
 * @return : 0，成功;其他负值,失败
 */
static int icm20608_remove(struct spi_device *spi)
{
	struct icm20608_dev *icm20608dev = spi_get_drvdata(spi);

	printk("%s\r\n", __FUNCTION__);

	/* 注销字符设备驱动 */
	/* 1、注销设备 */
	device_destroy(icm20608dev->class, icm20608dev->devid);
	/* 2、注销类 */
	class_destroy(icm20608dev->class); 
	/* 3、删除cdev */
	cdev_del(&icm20608dev->cdev);
	/* 4、注销设备号 */
	unregister_chrdev_region(icm20608dev->devid, ICM20608_CNT); 
	return 0;
}

/* 传统匹配方式ID列表 */
static const struct spi_device_id icm20608_id[] = {
	{"alientek,icm20608", 0},
	{}
};

/* 设备树匹配列表 */
static const struct of_device_id icm20608_of_match[] = {
	{ .compatible = "alientek,icm20608" },
	{ /* Sentinel */ }
};

/* SPI驱动结构体 */
static struct spi_driver icm20608_driver = {
	.probe = icm20608_probe,
	.remove = icm20608_remove,
	.driver = {
			.owner = THIS_MODULE,
			.name = "icm20608",
			.of_match_table = icm20608_of_match,
			},
	.id_table = icm20608_id,
};

/*
 * @description : 驱动入口函数
 * @param : 无
 * @return : 无
 */
static int __init icm20608_init(void)
{
	printk("%s\r\n", __FUNCTION__);
	return spi_register_driver(&icm20608_driver);
}

/*
 * @description : 驱动出口函数
 * @param : 无
 * @return : 无
 */
static void __exit icm20608_exit(void)
{
	printk("%s\r\n", __FUNCTION__);
	spi_unregister_driver(&icm20608_driver);
}

module_init(icm20608_init);
module_exit(icm20608_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ALIENTEK");
MODULE_INFO(intree, "Y");

